package org.dei.perla.sys.core;

import java.util.ArrayList;

import org.dei.perla.component.Component;
import org.dei.perla.sys.core.exception.AgentRegisrtyException;
import org.dei.perla.utils.pipe.Pipe;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;




public abstract class DefaultAgent<I,O> extends Component implements Runnable, Pluggable<I,O> {
	
	protected int idAgent;
	protected static final String DEFAULT_PIPE_NAME = "defaultPipe";
	protected Pipe<I> pipeInput; 
	protected Pipe<O> pipeOutput;
	protected long birthDate = -1L;
	protected long deathDate = -1L;
	
	
	
	public final long getBirthDate() {
		return birthDate;
	}

	public final long getDeathDate() {
		return deathDate;
	}

	public DefaultAgent(String parName) {
		super(parName);
	}
	
	@Override
	public final void run() {
		try{
			this.start();
			birthDate = System.currentTimeMillis();
			this.loop();
		}catch(Exception e){
			this.stop();
			e.printStackTrace();
			deathDate = System.currentTimeMillis();
			try {
				AgentRegistry.changeState(this, AgentState.DEAD);
			} catch (AgentRegisrtyException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
	}

	@Override
	public void setInputPipe(Pipe<I> pipe) {
		this.pipeInput = pipe;
	}

	@Override
	public void setOutputPipe(Pipe<O> pipe) {
		this.pipeOutput = pipe;
	}

	protected abstract void loop() throws Exception;

	
	public  Pipe<I> getInputPipe() {
		return pipeInput;
	}
	
	public  Pipe<O> getOutputPipe() {
		return pipeOutput;
	}
	

	/**
	 * Collega due agent tramite una pipe precedentemente istanziata. in particolare collega l'output pipe di a con l'input pipe di b 
	 * @param agentA e' l'agent sorgente di dati (fornisce la pipe di output)
	 * @param agentB e' l'agent consumatore di dati (fornisce la pipe di intput)
	 * @param pipe e' la pipe usata per la comunicazione fra i due agent. Deve essere istanziata. Il metoto invoca pipe.start()
	 * @see Pipe
	 * */
	@SuppressWarnings("unchecked")
	public static void bind(Pluggable agentA, Pluggable agentB, Pipe pipe){
		agentA.setOutputPipe(pipe);
		agentB.setInputPipe(pipe);
		pipe.start();
	}

	@Override
	public void start() {
		super.start();
		try {
			AgentRegistry.changeState(this, AgentState.RUNNING);
		} catch (AgentRegisrtyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void stop() {
		super.stop();
		try {
			AgentRegistry.changeState(this, AgentState.STOPPED);
		} catch (AgentRegisrtyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void subscribe() throws AgentRegisrtyException{
		this.idAgent = AgentRegistry.getFreeId();
		AgentRegistry.addToRegistry(this, AgentState.STOPPED, idAgent);
	}
	

	public abstract void respawn();
	public abstract ArrayList<String[]> inspect();
	
	public ArrayList<String[]> inspect(String args){
		throw new NotImplementedException();
	}

	public ArrayList<String[]> inspect(String args[]){
		throw new NotImplementedException();
	}
	
	public ArrayList<String[]> inspect(ArrayList<String> args){
		throw new NotImplementedException();
	}
	
	
	final public int getID(){
		return idAgent;
	}
}
